using System;
using System.Collections;
using System.Collections.Generic;
using CorruptedSmileStudio.MessageBox;
using Flaschengeist.Entities;
using Flaschengeist.Processes;
using Svelto.Tasks;
using UnityEngine;

namespace Flaschengeist.Views 
{
	public class GameView : BaseView
	{			
		#region Member
		private GameObject currentGlas;
		private GameObject fillBottle;
		private ProgressView timeProgressView;
		private ProgressView fluidProgressView;
		private ErrorView errorView;
		private List<GameObject> glasses;
		private IDictionary<string, AudioClip> audios;
		private int remainingFluid;
		private int remainingTime;
			
		private Level level;		
		private bool leftClickPerformed;
		private bool rightClickPerformed;		
		private bool isTaskRunning;
		private bool levelFinished;	
		private Texture2D background;
		private GUISkin skin;
		#endregion
				
		#region Initialization	
		public void Start()
		{
			audios = new Dictionary<string, AudioClip>();
			glasses = new List<GameObject>();
			timeProgressView = gameObject.AddComponent<ProgressView>();
			fluidProgressView = gameObject.AddComponent<ProgressView>();
			errorView = gameObject.GetComponent<ErrorView>();				
			skin = (GUISkin)Resources.Load (Settings.GUI_SKIN_GAME);
			background = (Texture2D)Resources.Load("textures/gui_levelInfoBackground");
		}					
		#endregion
				
		#region GUI					
		public override void Show()
		{
			fluidProgressView.Show();
			timeProgressView.Show();
			base.Show();
		}
		
		public override void Hide()
		{
			fluidProgressView.Hide();
			timeProgressView.Hide();
			base.Hide();
		}
			
		public void OnGUI()
		{	
			
			if (IsActive)	
			{	
				try 
				{
					GUI.skin = skin;
					GUI.DrawTexture(new Rect(10, 10, 200, 200),background);
					
					PaintButtons();
					PaintLabels();			
					AcceptBottleClicks();
					AcceptCameraMovement();					
					
					if (fillBottle != null)
						PlayBottleFillAnimation();
				} 
				catch (Exception e) 
				{
					errorView.Show(e);
				}		
			}
		}
				
		private void PaintButtons()
		{	
			if (GUI.Button(new Rect(40, 125, 140, 20), "own track"))
				ButtonPlayTrackClicked();
				
			if (GUI.Button(new Rect(40, 160, 140, 20), "level track"))
				ButtonPlayLevelTrackClicked();
		}
		
		private void PaintLabels()
		{
			//Label aktuelle Welt	
			GUI.Label(new Rect(25,15,200,30), level.World);
						
			//Label Zeit				
			GUI.Label(new Rect(85,54,200,30), "liquid");
						
			//Label Zeit
			GUI.Label(new Rect(90,84,200,30), "time");
		}
				
		private void AcceptCameraMovement()
		{
			var newPosition = Vector3.zero;    
			
			// movement with mouse
			if (Input.mousePosition.x < Settings.CAMERA_SCROLL_WIDTH && camera.transform.position.x > Settings.CAMERA_BORDER_LEFT)
				newPosition += Vector3.right * - Settings.CAMERA_SCROLL_SPEED * Time.deltaTime;
	 
			if (Input.mousePosition.x >= Screen.width - Settings.CAMERA_SCROLL_WIDTH && camera.transform.position.x < Settings.CAMERA_BORDER_RIGHT)
				newPosition += Vector3.right * Settings.CAMERA_SCROLL_SPEED * Time.deltaTime;
	 
			camera.transform.position += newPosition;
		}
		
		#endregion
		
		#region Update Data		
		public void UpdateLevel(Level level)
		{	
			this.level = level;
			this.levelFinished = false;
			this.remainingTime = level.TotalTimeSec;
			
			DestroyGlasses();
			CreateGlasses();
						
			fluidProgressView.SetProgress(level.TotalFluid);
			fluidProgressView.Init(level.TotalFluid, new Vector2(40, 55));				
			timeProgressView.Init(level.TotalTimeSec, new Vector2(40, 85));
			
			var task = new SingleTask(CountDownTime(), FinishLevel);
			TaskRunner.Instance.Run(task);
			Show();
		}
		
		public void UpdateRemainingFluid(int remainingFluid)
		{
			this.remainingFluid = remainingFluid;
			fluidProgressView.SetProgress(remainingFluid);
			
			if (remainingFluid == 0)
				FinishLevel();
		}
				
		public void UpdateNote(string note)
		{
			currentGlas.audio.clip = audios[note];			
			currentGlas.audio.clip.name = note;		
		}	
		
		public void UpdateAudios(IDictionary<string, AudioClip> audios)
		{
			this.audios = audios;
		}
		#endregion
	
		#region Functions					
		public void Stop()
		{
			TaskRunner.Instance.Stop();
		}
		
		public void Continue()
		{
			TaskRunner.Instance.Run(new SingleTask(CountDownTime(), FinishLevel));
		}
		
		
		
		public void FinishLevel()
		{	
			if (!levelFinished)
			{
				var waitForFinish = new SingleTask(WaitForFinish(), () => 
				{	
					var result = new LevelResult();
					result.Id = level.Id;
					result.Track = ConvertGlassesToTrack.Process(glasses);
					result.LevelTrack = level.Track;
					result.RemainingFluid = remainingFluid;
					result.TotalFluid = level.TotalFluid;
					result.RemainingTimeSec = remainingTime;
					result.TotalTimeSec = level.TotalTimeSec;
					result.Scale = level.Scale;
					result.MaxFillLevel = (int)level.MaxFillLevel;

					TaskRunner.Instance.Stop();
					levelFinished = true;
					Hide();
					Finish(result);
				});
				TaskRunner.Instance.Run(waitForFinish);				
			}					
		}
		
		private IEnumerator WaitForFinish()
		{
			while (isTaskRunning) 
				yield return new WaitForSeconds(0.1f);
		}
		
		private IEnumerator CountDownTime()
		{
			while (remainingTime > 0) 
			{
				timeProgressView.SetProgress(remainingTime);
				remainingTime -= 1;
				yield return new WaitForSeconds(1);
			}
			timeProgressView.SetProgress(remainingTime);
		}
		
		private IEnumerator PlayTrack()
		{						
			isTaskRunning = true;	
			
			for (int i = 0; i < glasses.Count; i++) 
			{
				if (glasses[i].particleSystem != null)
				{
					//Note korrekt
					if (glasses[i].audio.clip.name == level.Track[i])
						glasses[i].particleSystem.startColor = Color.green;
					else
						glasses[i].particleSystem.startColor = Color.red;
					
					glasses[i].particleSystem.Play();
				}
				
				glasses[i].audio.Play();
				yield return new WaitForSeconds(Settings.PLAY_AUDIO_DELAY);			
			}
			
			isTaskRunning = false;
		}
				
		private IEnumerator PlayLevelTrack()
		{								
			isTaskRunning = true;
			foreach (string note in level.Track) 
			{
				Camera.main.audio.clip = audios[note];
				Camera.main.audio.Play();
				yield return new WaitForSeconds(Settings.PLAY_AUDIO_DELAY);
			}
			isTaskRunning = false;
		}	
		
		private void DestroyGlasses()
		{
			foreach (GameObject glas in glasses)
			{
				Debug.Log("Destroy " + glas.name);
				Destroy(glas);
			}					
			glasses.Clear();
		}
		
		private void CreateGlasses()
		{
			for(int i = 0; i < level.Track.Length; i++)
			{
				var glas = (GameObject)Instantiate(Resources.Load(Settings.GLAS_PREFAB + level.WorldId), 
					new Vector3(0 + i,0,0), 
					new Quaternion(0,0,0,0));
				
				glas.name = Settings.GLAS_PREFAB+i.ToString();
				var audioSource = glas.AddComponent<AudioSource>();
				audioSource.clip = audios[level.Scale[0]];
				audioSource.clip.name = level.Scale[0];
				glasses.Add(glas);
			}
		}
		
		private void BeginFillGlas(GameObject glas)
		{			
			Debug.Log("Fill " + glas.name);
			isTaskRunning = true;
			
			fillBottle = (GameObject)Instantiate(Resources.Load(Settings.BOTTLE_PREFAB + level.WorldId), glas.transform.position + new Vector3(1.15f -(0.1f * level.WorldId), 2.0f, 0.0f), new Quaternion(0,0,0,0));
			fillBottle.name = Settings.BOTTLE_PREFAB;
			fillBottle.transform.Rotate(270,0,0);
			
			FillLevelChanging(glas.animation[level.GlasAnimation].time * 100);		
			PlayGlasFillAnimation(glas, 1.0f);
		}

		private void BeginReleaseGlas(GameObject glas)
		{
			if (glas.animation[level.GlasAnimation].time > 0)
			{		
				Debug.Log("Release " + glas.name);
				isTaskRunning = true;
				FillLevelChanging(glas.animation[level.GlasAnimation].time * 100);
				PlayGlasFillAnimation(glas, -1.0f);		
			}
		}

		private void EndFillGlas(GameObject glas)
		{	
			Destroy(fillBottle);			
			//Maximale Füllmenge überschritten		       
			if (glas.animation[level.GlasAnimation].time * 100 > level.MaxFillLevel)	           
				glas.animation[level.GlasAnimation].time = level.MaxFillLevel / 100;
			
			Debug.Log(string.Format("{0}: {1}ml", glas.name, glas.animation[level.GlasAnimation].time * 100));	
			GlasFilled(glas.animation[level.GlasAnimation].time * 100);			
			PlayGlasFillAnimation(glas, 0);
			glas.audio.Play();
			isTaskRunning = false;
		}

		private void EndReleaseGlas(GameObject glas)
		{	   			    
			//Minimale Füllmenge unterschritten			
			if (glas.animation[level.GlasAnimation].time < 0)
				glas.animation[level.GlasAnimation].time = 0;	
						
			Debug.Log(string.Format("{0}: {1}ml", glas.name, glas.animation[level.GlasAnimation].time * 100));	
			GlasReleased(glas.animation[level.GlasAnimation].time * 100);			
			PlayGlasFillAnimation(glas, 0);
			glas.audio.Play();
			isTaskRunning = false;
		}	
		#endregion
		
		#region Animation		
		private void PlayBottleFillAnimation()
		{			
			if(fillBottle.transform.localEulerAngles.x > 269.0f ||  fillBottle.transform.localEulerAngles.x < 30.0f)
				fillBottle.transform.Rotate(0.0f,-6.0f,0.0f);
		}

		private void PlayGlasFillAnimation(GameObject glas, float speed)
		{					
			glas.animation[level.GlasAnimation].speed = speed;
			glas.animation.Play(level.GlasAnimation);
		}	
		#endregion

		#region Handle Clicks		
		private void ButtonPlayLevelTrackClicked()
		{					
			Hide();					
			var task = new SingleTask(PlayLevelTrack(), () => Show());
			TaskRunner.Instance.Run(task);
		}
		
		private void ButtonPlayTrackClicked()
		{
			Hide();		
			var currentTrack = ConvertGlassesToTrack.Process(glasses);
			var task = new SingleTask(PlayTrack(), () => CompareTracks(level.Track, currentTrack));										
			TaskRunner.Instance.Run(task);
		}
		
		private void AcceptBottleClicks()     
		{   
			RaycastHit click;
	                            
			if(Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out click))      
			{     				
				if(Input.GetMouseButtonDown(0) && !rightClickPerformed && !leftClickPerformed)
				{				
					if (click.collider.gameObject != null)
					{
						currentGlas = click.collider.gameObject;					
						leftClickPerformed = true;					
						BeginFillGlas(currentGlas);							
					}
				}
				else if (Input.GetMouseButtonDown(1) && !rightClickPerformed && !leftClickPerformed)	
				{
					if (click.collider.gameObject != null)
					{
						currentGlas = click.collider.gameObject;
						rightClickPerformed = true;
						BeginReleaseGlas(currentGlas);     
					}
				}
			}
			
			if (Input.GetMouseButtonUp(0) && leftClickPerformed)
			{
				EndFillGlas(currentGlas);
				leftClickPerformed = false;
			}
			
			else if (Input.GetMouseButtonUp(1) && rightClickPerformed)			
			{				
				if (currentGlas.audio != null)				
					EndReleaseGlas(currentGlas);		
				
				rightClickPerformed = false;
			}	
		}					     					
		#endregion
				
		#region Events		
		public event Action<float> FillLevelChanging;
		public event Action<float> GlasFilled;
		public event Action<float> GlasReleased;
		public event Action<string[], string[]> CompareTracks;
		public event Action<LevelResult> Finish;
		#endregion
	}
}

